#ifndef XPM_DOC_MODEL_XPM_TERM_DOC_H
#define XPM_DOC_MODEL_XPM_TERM_DOC_H

#include <algorithm>
#include <list>
#include <string>
#include <iostream>
#include "tree.h"
#include "XpmTermBlock.h"
#include "model/Term.h"
#include "XpmClipboard.h"

namespace xpm {	
	using namespace std;	
	
	class XpmTermDoc {
	public:
		XpmTermDoc() : _lang("en") {}
		XpmTermDoc(string lang) : _lang(lang) {}
		~XpmTermDoc() {Clear();}
		void Clear();
		//inferfaces for generated tree after the format analysising
		//blocks management
		TermBlocks& GetTermBlocks();
		TermBlocksConstIter begin() const;
		TermBlocksConstIter end() const;
		TermBlocksIter GetTermBlock(int pos);
		TermBlocksIter GetTermBlockInvolvesPos(int pos);
		TermBlocksIter GetTermBlockBeforePos(int pos);
		TermBlocksIter GetTermBlock(int pos, TermBlocksIter start);
		int RemoveTermBlock(int pos);		
		//###################################################
		// very important interface to find a list of termrange
		vector<TermRange> findTermRanges(int pos, int len) const;
		//interface for add map from a range to a term
		RemainRangePair AddMap(int word_ix, int block_ix, wstring text, conex::Term* term);
		//interface for remove map in the doc
		RemainRange RemoveMap(int word_ix, int block_ix);
		//Add a list of blocks from clipboard into the document
		void InsertXpmBlocksFromClipboard(int blockpos, int startpos, XpmClipboard * clipboard);
		//get blocks from the selection
		void GetXpmBlocksFromSelectionToClipboard(int start, int end, XpmClipboard * clipboard);
		// copy old block to new block
		void CopyBlock(int start, int end, XpmTermBlock * oldblock, XpmTermBlock * newblock);
		// copy first block
		void PasteFirstBlock(int start, XpmTermBlock* docblock, XpmTermBlock* clipboardblock) ;
		//remove for single character 
		//when the textdocument's contents changed. we need to update the block lists
		//remove for a range
		void RemoveSomethingBefore(int startpos, int charsRemoved);
		RemainRangePair RemoveCharsInPosRange(int startpos, int chardRemoved);
		//we need to update the block lists
		void AddSomethingBefore(int startpos, int charsAdded);
		RemainRangePair AddCharsInPosRange(int startpos, int charsAdded);	
		//this is used for tooltip functiion
		TermRange GetTermRangeFromPos(int pos);
		//precise find three seleciton range
		//void FindByRange(SelectionRange* range, TermBlocksIter &block, TermIter &term);
		//##################################################
		//this is used to parse the xml data
		XpmTermBlock* CreateBlockAtEnd();
		//copy from a xpmtermblock to my xpmtermblock
		XpmTermBlock* CreateBlockAtEnd(XpmTermBlock* block);
		conex::Term* GetTermInStartPos(int startpos);
		TermRange GetTermRangeInStartPos(int startpos);		
		int totalMaps();
		// lang set and get
		string lang() const;
		void setLang(string lang);
		// operator assign
		XpmTermDoc& operator=( const XpmTermDoc& rhs );
	private:		
		//essitinal attributes
		TermBlocks _termblocks;
		string _lang;
	};	
	inline TermBlocksConstIter XpmTermDoc::begin() const {
		return _termblocks.begin();
	}
	inline TermBlocksConstIter XpmTermDoc::end() const {
		return _termblocks.end();
	}
	inline XpmTermBlock* XpmTermDoc::CreateBlockAtEnd(XpmTermBlock* block) {
		XpmTermBlock* _block = new XpmTermBlock(*block);
		_termblocks.push_back(_block);
		return block;
	}
	inline XpmTermDoc& XpmTermDoc::operator=( const XpmTermDoc& rhs ) {
		for(TermBlocksConstIter it = rhs.begin(); it !=  rhs.end(); ++it) {
		    CreateBlockAtEnd(*it);
		}
		_lang = rhs._lang;
		return *this;
	}
	inline string XpmTermDoc::lang() const {
		return _lang;
	}
	inline void XpmTermDoc::setLang(string lang) {
		_lang = lang;
	}
	inline conex::Term* XpmTermDoc::GetTermInStartPos(int startpos) {
		TermRange range = GetTermRangeInStartPos(startpos);
		return range.term();		
	}
	inline TermRange XpmTermDoc::GetTermRangeInStartPos(int startpos) {
		return GetTermRangeFromPos(startpos);
	}
	inline TermRange XpmTermDoc::GetTermRangeFromPos(int pos) {
		TermBlocksIter it = GetTermBlockInvolvesPos(pos);
		if(it != _termblocks.end()) {
			cout << "find the block" << endl;
			TermIter termIter = (*it)->GetTermIterByPos(pos);
			if(termIter != (*it)->GetTerms().end()) {
				return *termIter;
			}
		}
		TermRange range;
		return range;
	}
	inline TermBlocks& XpmTermDoc::GetTermBlocks() {
		return _termblocks;
	}
	inline TermBlocksIter XpmTermDoc::GetTermBlock(int pos) {
		return GetTermBlock(pos, _termblocks.begin());
	}	
	inline int XpmTermDoc::RemoveTermBlock(int pos) {
		TermBlocksIter it = GetTermBlock(pos);
		if(it != _termblocks.end()) {
			_termblocks.erase(it);
		}
		return true;
	}
	inline XpmTermBlock* XpmTermDoc::CreateBlockAtEnd() {
		XpmTermBlock* block = new XpmTermBlock(0);
		_termblocks.push_back(block);
		return block;
	}
	inline void XpmTermDoc::Clear() {
		if(!_termblocks.empty()) {
			for(TermBlocksIter it = _termblocks.begin(); it != _termblocks.end(); ++it) {
				if(*it != 0) {
					delete *it;
					*it = 0;
				}
			}
			_termblocks.clear();
		}
	}
}
#endif